/** 基于dbm的加密存储类
 * @file aidb.hpp
 * @author Kiven Lee
 * @version 1.0
 * @date 2022-04-08
 *
 * @copyright Kivensoft (c) 2018 - 2022
 *
 */

#pragma once

#include "klib/dbm.hpp"
#include "klib/net.hpp"
#include "klib/log.hpp"
#include "klib/utils.hpp"

#include <cstdint>
#include <cstring>
#include <utility>
#include <string>
#include <string_view>
#include <memory>

namespace ai {

    constexpr std::string_view AIDB_EXT("aidb");

    using aidb_key_t = uint32_t;

    struct aidb_t;

    namespace _aidb {

        /** 超级块，记录整个数据库的基本信息 */
        struct _super_block_t {
            char        magic[4];       // 魔数，固定为 aidb
            uint8_t     version[2];     // 版本号, 第一个字节是主版本号，第二个字节是副版本号
            uint8_t     salt[2];        // 签名盐值，生成md5签名的盐值
            uint32_t    last_key;       // 最后一次新增的id，用于新建记录时+1插入
            uint32_t    total;          // 总记录数
            uint64_t    modified;       // 最后修改时间（Unix时间戳）
            char        sign[16];       // 签名，用于校验key是否正确
            uint8_t     iv[16];         // aes加密向量，创建数据库时随机生成
            char        reserve[8];     // 保留字节
        };

        constexpr aidb_key_t _SUPER_BLOCK_KEY = klib::net::n2h(0);    // 超级块的键值
        constexpr uint32_t _AIDB_BLOCK_SIZE = 512;  // 数据库区块大小

    } // namespace _aidb

    struct aidb_value_t : public klib::dbm::uni_value_t {
        string_view title;
        string_view user;
        string_view pass;
        string_view url;
        string_view memo;

        aidb_value_t() = default;

        aidb_value_t(string_view title, string_view user, string_view pass, string_view url, string_view memo);

        aidb_value_t(klib::dbm::uni_value_t&& val);

        ~aidb_value_t() = default;

        aidb_value_t(aidb_value_t&& val): uni_value_t(std::move(val)),
                title(val.title), user(val.user), pass(val.pass), url(val.url), memo(val.memo) {}

        aidb_value_t& operator= (aidb_value_t&& val) {
            this->~aidb_value_t();
            return *(new (this) aidb_value_t(std::move(val)));
        }
    };

    // 数据库的遍历指针
    struct aidb_iterator_t {

        aidb_key_t get() const noexcept { return _key; }
        aidb_key_t operator *() const noexcept { return get(); }

        aidb_iterator_t& operator ++();
        aidb_iterator_t  operator ++(int);

        bool operator ==(const aidb_iterator_t& iter) const {
            return _key == iter._key && _db == iter._db;
        }

    protected:
        aidb_t*     _db;
        aidb_key_t  _key;

        aidb_iterator_t(aidb_t* db, aidb_key_t key): _db(db), _key(key) {}

        friend struct aidb_t;
    };

    /** aidb数据库类 */
    struct aidb_t {
        using dbm_t = klib::dbm::dbm_t;
        using open_mode_t = klib::dbm::open_mode_t;
        using store_mode_t = klib::dbm::store_mode_t;
        using value_t = klib::dbm::value_t;
        // using uni_value_t = klib::dbm::uni_value_t;
        using super_block_t = _aidb::_super_block_t;

        enum class opstate {ok, db_not_exists, db_exists, can_not_open, can_not_read, can_not_write,
                super_block_not_exists, super_block_size_error, password_error};

        struct aidb_key_value_t : public value_t {
            aidb_key_t key;
            aidb_key_value_t(aidb_key_t key): value_t(&this->key, sizeof(aidb_key_t)), key(klib::net::h2n(key)) {}
            aidb_key_value_t(std::in_place_t, aidb_key_t key): value_t(&this->key, sizeof(aidb_key_t)), key(klib::net::n2h(key)) {}
        };

        dbm_t           db;
        uint8_t         db_key[16];
        super_block_t   super_block;

        static constexpr aidb_key_t nkey = _aidb::_SUPER_BLOCK_KEY;

        aidb_t() {}

        aidb_t(const string& filename, string_view password): aidb_t() { create(filename, password); }

        aidb_t(const aidb_t&) = delete;
        aidb_t& operator= (const aidb_t&) = delete;

        bool is_opened() const noexcept { return db.is_opened(); }
        operator bool() const noexcept { return db.is_opened(); }

        const char* dbname() const noexcept { return db.dbname(); }

        opstate create(const string& filename, string_view password);

        opstate open(const string& filename, string_view password);

        void close() { db.close(); }

        aidb_key_t put(aidb_key_t key, const aidb_value_t& value) {
            if (key == nkey) {
                key = (aidb_key_t) ++super_block.last_key;
                if (!_update_super_block())
                    return nkey;
            }
            return _put(key, value.data, value.size) ? key : nkey;
        }

        /** 插入新纪录，返回0表示插入失败 */
        aidb_key_t put(const aidb_value_t& value) {
            aidb_key_t key = (aidb_key_t) ++super_block.last_key;
            return _update_super_block() && _put(key, value.data, value.size) ? key : nkey;
        }

        aidb_value_t get(aidb_key_t key);

        bool erase(aidb_key_t key) { return db.erase(aidb_key_value_t(key)); }

        bool exists(aidb_key_t key) { return db.exists(aidb_key_value_t(key)); }

        bool reorganize() { return db.reorganize(); }

        aidb_key_t firstkey();

        aidb_key_t nextkey(aidb_key_t key);

        aidb_iterator_t begin() { return aidb_iterator_t(this, firstkey()); }
        aidb_iterator_t end() { return aidb_iterator_t(this, nkey); }

    protected:
        bool _put(const aidb_key_t key, const void* data, uint32_t size);

        bool _update_super_block() {
            super_block.modified = (uint64_t) std::time(nullptr);
            return db.store(aidb_key_value_t{nkey}, value_t{&super_block});
        }
    };


    inline aidb_iterator_t& aidb_iterator_t::operator ++() {
            _key = _db->nextkey(_key);
            return *this;
        }

    inline aidb_iterator_t  aidb_iterator_t::operator ++(int) {
        aidb_key_t k = _key;
        _key = _db->nextkey(_key);
        return aidb_iterator_t(_db, k);
    }



} // namespace ai